"""
文本的主要两个类型是 string和 object。如果不特殊指定类型为 string，文本类型一般为 object。

文本的操作主要是通过访问器 str 来实现的，功能十分强大，但使用前需要注意以下几点。
    1.访问器只能对 Series 数据结构使用。 除了常规列变量 df.col 以外，也可以对索引类型 df.Index和 df.columns使用
    2.确保访问的对象类型是字符串 str类型。 如果不是需要先 astype(str)转换类型，否则会报错
    3.访问器可以多个连接使用。 如 df.col.str.lower().str.upper()，这个和 Dataframe中的一行操作是一个原理
"""
"""
文本处理
    文本提取：extract
    文本查询：find、findall
    文本包含：contains
    文本的虚拟变量：get_dummies
    文本格式
    文本拆分：split
    文本替换：replace、slice_replace、repeat
    文本拼接：cat
"""
import pandas as pd
import numpy as np
import warnings

warnings.filterwarnings("ignore")

df = pd.DataFrame(data={
    'name': ['jordon', 'MIKE', 'Kelvin', 'xiaoLi', 'qiqi', 'Amei'],
    'Age': [18, 30, 45, 23, 45, 62],
    'level': ['high', 'Low', 'M', 'L', 'middle', np.nan],
    'Email': ['jordon@sohu.com', 'Mike@126.cn', 'KelvinChai@gmail.com', 'xiaoli@163.com', np.nan, 'amei@qq.com']
})
print(df)


def 文本格式():
    """
        大小写变换

    s.str.lower()  字符串全部变小写
    s.str.upper()  字符串全部变大写
    s.str.title()   每个单词首字母变大写
    s.str.capitalize()  字符串第一个字母大写
    s.str.swapcase()    大小写字母转换
    """
    print(df.columns.str.lower(), "columns变成小写")

    """
        格式判断
        
    s.str.center(8,fillchar="*")    居中对齐，宽度为 8，其余用"*"填充
    s.str.ljust(8, fillchar='*')    左对齐，宽度为8，其余用’*’填充
    s.str.rjust(8, fillchar='*')    右对齐，宽度为8，其余用’*’填充
    s.str.pad(width=8, side='both',fillchar='*')    自定义对齐方式，参数可调整宽度、对齐方向、填充字符
    """
    print(df["name"].str.center(width=8, fillchar="*"))

    """
        计数和编码

    s.str.count('b')    字符串种包括指定字母的数量
    s.str.len()    字符串长度
    s.str.encode('utf-8')   字符编码
    s.str.decode('utf-8')   字符解码
    """


def 文本拆分():
    """
    通过使用 split 方法可以某个指定的字符作为分割点拆分文本。
    其中，
        expand 参数可以让拆分的内容展开，形成单独的列，
        n 参数可以指定拆分的位置来控制形成几列。
    s.str.split('x', expand=True, n=1)
    """
    print("将email变量按照@进行拆分\n", df["Email"].str.split("@"))
    print("将email变量按照@进行拆分，并展开\n", df["Email"].str.split("@", expand=True))
    print("通过正则表达式将email变量按照 @ 和 . 进行拆分，并展开\n", df["Email"].str.split("\@|\.", expand=True))


def 文本替换():
    """
    文本替换有几种方法：replace，slice_replace，repeat
    """
    """
        replace替换
        
    replace方法是最常用的替换方法，参数如下：
        被替代的内容字符串，也可以为正则表达式
        新内容字符串，也可以是一个被调用的函数
        regex：用于设置是否支持正则，默认是True
    """
    print("将email种的com都替换为cn\n", df["Email"].str.replace("com", "cn"))

    # 将旧内容写成正则表达式
    print("将@之前的名字都替换成xxx\n", df["Email"].str.replace("(.*?)@", "xxx@"))

    # 将新内容写成被调用的函数
    print("将@之前的名字都大写\n", df["Email"].str.replace("(.*?)@", lambda x: x.group().upper()))

    """
        切片替换

    slice_replace 通过切片的方式实现替换，通过切片可以保留或者删除指定的字符，参数如下。
        start：起始位置
        stop：结束位置
        repl：要替换用的新内容
        
    对 start 切片位置之后和 stop 切片位置之前进行替换，如果没有设置 stop，那么 start 之后全部进行替换，
    同理如果没设置 start，那么 stop 之前全部进行替换。
    """
    print(df["Email"].str.slice_replace(start=1, stop=2, repl="xx"))

    """
    重复替换

    repeat 可以实现重复替换的功能，参数repeats设置重复的次数。
    """
    print(df["name"].str.repeat(repeats=2))


def 文本拼接():
    """
    文本拼接通过cat方法实现，参数：
        others: 需要拼接的序列，如果为 None不设置，就会自动把当前序列拼接为一个字符串
        sep: 拼接用的分隔符
        na_rep: 默认不对空值处理，这里设置空值的替换字符。
        join: 拼接的方向，包括 left, right, outer, inner，默认为left
    """
    # 将单个序列拼接为一个完整的字符串
    # 当没有设置ohters参数时，该方法会将当前序列合并为一个新的字符串
    print(df["name"].str.cat())
    # 设置sep分隔符为`-`
    print(df["name"].str.cat(sep="-"))
    # 将缺失值赋值为`*`
    print(df["level"].str.cat(sep="-", na_rep="*"))

    # 拼接序列和其他类列表型对象为新的序列
    # 将name列和*列拼接，再将level列拼接，形成一个新的序列。
    print("str.cat多级连接实现多列拼接\n", df["name"].str.cat(["*"] * 6).str.cat(df["level"]))
    print("也可以直接多列拼接\n", df["name"].str.cat([df["level"], df["Email"]], na_rep="*"))


def 文本提取():
    """文本提取主要通过extract来实现的。

    extract 参数：
        pat: 通过正则表达式实现一个提取的pattern
        flags: 正则库re中的标识，比如 re.IGNORECASE
        expand: 当正则只提取一个内容时，如果expand=True会展开返回一个DataFrame，否则返回一个Series
    """
    print("提取Email 中的两个内容", df["Email"].str.extract(pat="(.*?)@(.*?).com"))


def 文本查询():
    """通过 find 和 findall两个方法实现。
    find 参数很简单，直接输入要查询的字符串即可，返回在原字符串中的位置，没查询到结果返回-1。
    """
    df["@position"] = df["Email"].str.find("@")
    print("返回@在email变量中的位置\n", df[["Email", "@position"]])

    """findall参数：
    pat: 要查找的内容，支持正则表达式
    flag: 正则库re中的标识，比如re.IGNORECASE
    findall和find的区别是支持正则表达式，并返回具体内容。这个方法有点类似extract，也可以用于提取，但不如extract方便。
    """
    print("返回正则查找的两个部分，并以元组列表的形式出现\n", df["Email"].str.findall("(.*?)@(.*).com"))


def 文本包含():
    """
    文本包含通过 contains 方法实现，返回布尔值，一般和 loc查询功能配合使用，参数：

    pat: 匹配字符串，支持正则表达式
    case: 是否区分大小写，True 表示区别
    flags: 正则库 re 中的标识，比如 re.IGNORECASE
    na: 对缺失值填充
    regex: 是否支持正则，默认 True 支持

    """
    print(df["Email"].str.contains("jordon|com", na="*"))

    # 这里需要注意一下，如果和loc配合使用，注意不能有缺失值，否则会报错。可以通过设置 na=False 忽略缺失值完成查询。
    res = df.loc[df["Email"].str.contains("jordon|com", na=False), :]
    print(res)


def 文本的虚拟变量():
    """
        get_dummies可以将一个列变量自动生成虚拟变量（哑变量），这种方法在特征衍生中经常使用。
    """
    print(df["name"].str.get_dummies())

    # 哑变量 get_dummies 独热编码
    tmpdf = pd.DataFrame(data=[
        ['green', 'A'],
        ['red', 'B'],
        ['blue', 'A']
    ], columns=["color", "class"])
    print(tmpdf)
    tmpdf = pd.get_dummies(tmpdf, columns=["color"])
    print(tmpdf)


if __name__ == '__main__':
    # 文本格式()
    # 文本拆分()
    # 文本替换()
    # 文本拼接()
    # 文本提取()
    # 文本查询()
    # 文本包含()
    文本的虚拟变量()
